﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Behaviorize
{
	public class ScenarioVersion
	{
		internal readonly Scenario Scenario;

		private List<Exception> _exceptions;

		internal ScenarioVersion(Scenario scenario)
		{
			Scenario = scenario;
			ArrangementSteps = new List<ArrangementStep>();
			ActionSteps = new List<ActionStep>();
			AssertionSteps = new List<AssertionStep>();
		}

		internal List<ArrangementStep> ArrangementSteps { get; private set; }
		internal List<AssertionStep> AssertionSteps { get; private set; }
		internal List<ActionStep> ActionSteps { get; private set; }

		public IEnumerable<Exception> GetExceptions()
		{
			if (_exceptions == null)
			{
				throw new ScenarioVersionNotVerifiedException("Scenario version must be verified before checking for exceptions");
			}

			return _exceptions;
		}

		/// <summary>
		/// Executes all the steps, prints result to console and invokes the ReportMaker
		/// </summary>
		/// <returns>First exception that is encountered</returns>
		internal List<Exception> Verify()
		{
			_exceptions = new List<Exception>();

			foreach (ArrangementStep step in ArrangementSteps)
			{
				ExecuteStep(step);
			}

			foreach (ActionStep step in ActionSteps)
			{
				ExecuteStep(step);
			}

			foreach (AssertionStep step in AssertionSteps)
			{
				ExecuteStep(step);
			}

			return _exceptions;
		}

		private void ExecuteStep(Step step)
		{
			try
			{
				step.ExecuteStep();
				step.Outcome = OutcomeType.Passed;
			}
			catch (Exception e)
			{
				Exception rootException = e.InnerException;

				step.Outcome = OutcomeType.Failed;

				_exceptions.Add(rootException);
				step.FailureIndex = _exceptions.Count;
			}
		}

		public int GetSignature()
		{
			unchecked
			{
				int hash = Scenario.Name.GetHashCode();

				foreach (ArrangementStep arrangementStep in ArrangementSteps)
				{
					hash = hash * 23 + arrangementStep.GetHashCode();
				}

				foreach (ActionStep actionStep in ActionSteps)
				{
					hash = hash * 23 + actionStep.GetHashCode();
				}

				foreach (AssertionStep assertionStep in AssertionSteps)
				{
					hash = hash * 23 + assertionStep.GetHashCode();
				}

				return hash;
			}
		}
	}

	public class ScenarioVersionNotVerifiedException : Exception
	{
		public ScenarioVersionNotVerifiedException(string message) : base(message) {}
	}
}